#ifndef _ASMARM_PGALLOC_H
#define _ASMARM_PGALLOC_H

#include <linux/gfp.h>

#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/memory.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
#include <asm/domain.h>

#define PGALLOC_GFP	(GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)

#define _PAGE_USER_TABLE	(PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER))
#define _PAGE_KERNEL_TABLE	(PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_KERNEL))

#define pgd_alloc(mm)			get_pgd_slow(mm)

static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
{
	pte_t *pte;

	pte = (pte_t *)__get_free_page(PGALLOC_GFP);
	if (pte) {
		clean_dcache_area(pte, sizeof(pte_t) * PTRS_PER_PTE);
		pte += PTRS_PER_PTE;
	}

	return pte;
}

static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
{
	if (pte) {
		pte -= PTRS_PER_PTE;
		free_page((unsigned long)pte);
	}
}

static inline void __pmd_populate(pmd_t *pmdp, unsigned long pmdval)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	pmdp[0] = __pmd(pmdval);
	pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
	flush_pmd_entry(pmdp);
}

static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
{
	unsigned long pte_ptr = (unsigned long)ptep;

	/*
	 * The pmd must be loaded with the physical
	 * address of the PTE table
	 */
	pte_ptr -= PTRS_PER_PTE * sizeof(void *);
	__pmd_populate(pmdp, __pa(pte_ptr) | _PAGE_KERNEL_TABLE);
}

static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addr)
{
	struct page *pte;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// pte = alloc_pages(PGALLOC_GFP, 0);
	pte = alloc_pages(PGALLOC_GFP, 1);
	printf("this is %s(): %d	pte = 0x%x\r\n", __func__, __LINE__, pte);
	if (pte) {
		// if (!PageHighMem(pte)) {
		if (1) {
			void *page = page_address(pte);
			memset(page, 0, PAGE_SIZE);
			printf("this is %s(): %d	page = 0x%x\r\n", __func__, __LINE__, page);
			printf("this is %s(): %d	virt_to_phys(page) = 0x%x\r\n", __func__, __LINE__, virt_to_phys(page));
			printf("this is %s(): %d	*page = 0x%x\r\n", __func__, __LINE__, *((unsigned long *)page));
			clean_dcache_area(page, sizeof(pte_t) * PTRS_PER_PTE);
		}
		// pgtable_page_ctor(pte);
	}

	return pte;
}

static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	__pmd_populate(pmdp, page_to_pfn(ptep) << PAGE_SHIFT | _PAGE_USER_TABLE);
	// __pmd_populate(pmdp, (unsigned long)(ptep) | _PAGE_USER_TABLE);
}

#endif
